<?php

namespace app\common\controller;

use app\BaseController;
use app\model\AreasModel;
use think\App;

class Areas extends BaseController
{
    protected AreasModel $mode;
    public function __construct(App $app)
    {
        parent::__construct($app);
        $this->mode = new AreasModel();
    }

    /**
     * 获取所有省市区
     * @return false|string
     */
    public function getAllAreas(): bool|string
    {
        try {
            $data = $this->mode->field('id,name,pid,level')->select()->toArray();
            if (empty($data)) return success();
            $back = array();
            foreach ($data as $v){
                $back[$v['id']]=$v;
            }
            $back[0]=[];
            foreach ($back as &$item){
                if (isset($item['pid'])){
                    $back[$item['pid']]['child'][]=&$item;
                }
            }
            return success($back[0]['child']);
        } catch (\Exception $e) {
            return error($e->getMessage(),$e->getCode(),[
                'line' => $e->getLine(),
                'file' => $e->getFile()
            ]);
        }
    }

    /**
     * 获取部分地区
     * @return bool|string
     */
    public function getAreas(): bool|string
    {
        try {
            $param = $this->request->param();
            $this->validate($param,[
                'id|ID' => 'require|integer',
            ]);
            $info = $this->mode->field('id,pid,level,top_layer_id')->find($param['id']);
            if (!$info) return success();
            $info = $info->toArray();
            $data = [
                'level' => $info['level'],
                'province_id' => 0,
                'city_id' => 0,
                'area_id' => 0,
            ];
            switch ($info['level']) {
                case 1:
                    $data['province_id'] = $info['id'];
                    break;
                case 2:
                    $data['province_id'] = $info['pid'];
                    $data['city_id'] = $info['id'];
                    break;
                case 3:
                    $data['province_id'] = $info['top_layer_id'];
                    $data['city_id'] = $info['pid'];
                    $data['area_id'] = $info['id'];
                    break;
            }
            return success($data);
        } catch (\Exception $e) {
            return error($e->getMessage(),$e->getCode(),[
                'line' => $e->getLine(),
                'file' => $e->getFile()
            ]);
        }
    }

    /**
     * 获取层级树
     * @return bool|string
     */
    public function getLevelTree(): bool|string
    {
        try {
            $userLevel = $this->userData['level'] ?? 0;
            $areasId = $this->userData['areas_id'] ?? 0;

            $lock = 'getLevelTree_'.$userLevel;
            $data = cache($lock);
            if ($data) return success($data);
            // 1. 层级为3时（最底层）：仅查询当前地区，无需构建树
            if ($userLevel == 3 && $areasId) {
                // 单条记录查询用find()，直接返回数组（减少集合转数组的开销）
                $area = $this->mode->field('id,pid,name,level')
                    ->where('id', $areasId)
                    ->find();
                return success($area ? [$area->toArray()] : []);
            }

            // 2. 非层级3：一次性查询所需数据，减少数据库交互
            // 构建查询条件：仅查询用户层级及以下的有效数据（避免冗余数据）
            $query = $this->mode->field('id,pid,name,level')
                ->where('level', '>=', $userLevel);

            // 若有指定地区ID，通过IN查询一次性获取该地区及所有子级（利用层级关系过滤）
            if ($areasId) {
                // 先获取当前地区的层级，用于限制最大查询层级（避免查无关数据）
                $userArea = $this->mode->field('id,pid,name,level')->find($areasId);
                if (!$userArea) {
                    return success([]);
                }
                $userAreaArr = $userArea->toArray();
                $maxLevel = $userAreaArr['level'] + (3 - $userLevel); // 最多查至层级3
                $query->where('level', '<=', $maxLevel);
            }

            // 执行查询并转为索引数组（ID为键，减少后续查找开销）
            $baseData = $query->select()->toArray();
            $baseDataMap = array_column($baseData, null, 'id'); // 以ID为键构建映射表

            // 3. 内存中构建树形结构（利用映射表减少遍历次数）
            if ($areasId) {
                // 从映射表中直接获取用户地区（避免二次查询数据库）
                if (!isset($baseDataMap[$areasId])) {
                    return success([]);
                }
                $userAreaArr = $baseDataMap[$areasId];
                // 基于映射表构建子树（优化递归效率）
                $userAreaArr['child'] = $this->buildTreeFromMap($baseDataMap, $areasId);
                $data = [$userAreaArr];
            } else {
                // 无指定地区时，从根节点（pid=0）开始构建
                $data = $this->buildTreeFromMap($baseDataMap, 0);
            }
            cache($lock,$data,300);
            return success($data);
        } catch (\Exception $e) {
            return error($e->getMessage(), $e->getCode(), [
                'line' => $e->getLine(),
                'file' => $e->getFile()
            ]);
        }
    }

    /**
     * 基于ID映射表构建树形结构（非递归，减少内存开销）
     * @param array $dataMap 以ID为键的地区数据映射表
     * @param int $parentId 父级ID
     * @return array
     */
    private function buildTreeFromMap(array $dataMap, int $parentId): array
    {
        $tree = [];
        // 第一次遍历：为每个节点挂载子节点（利用映射表直接定位）
        foreach ($dataMap as $id => $item) {
            if ($item['pid'] == $parentId) {
                $tree[] = &$dataMap[$id]; // 引用传递，避免复制数组
            } elseif (isset($dataMap[$item['pid']])) {
                // 子节点挂载到父节点的child中
                $dataMap[$item['pid']]['child'][] = &$dataMap[$id];
            }
        }
        // 解除引用，避免后续操作污染原数组
        unset($dataMap);
        return $tree;
    }

}
